package robocup.component.worldmodel;

import robocup.component.WorldModel;
import robocup.component.ControllerNumbers.PLAY_MODE;
import robocup.component.ControllerNumbers.REFEREE_MESSAGE;
import robocup.component.infotypes.InfoReceiver;
import robocup.component.infotypes.PlayModeInfo;
import robocup.component.infotypes.PlayerSayInfo;
import robocup.component.infotypes.PlayerTypesInfo;
import robocup.component.infotypes.RefereeInfo;
import robocup.component.infotypes.SenseBodyInfo;
import robocup.component.infotypes.VisualInfo;
import robocup.component.speechacts.MessageFactory.MESSAGES;
import robocup.component.tactics.AbstractState.STATES;

/**
 * This Model updates and stores the playmode. In most cases an update is
 * trivial as the new playmode is diretly communicated by the SServer. However,
 * sometimes those messages are missed by the player. We try to avoid these
 * cases by using inderict updates (i.e. a moving ball is only possible in
 * PLAY_MODE==PLAY_ON).
 * 
 */
public class PlayModeModel
    implements InfoReceiver {

  private final WorldModel      world;

  // the last heared refereee message type
  private REFEREE_MESSAGE lastRefereeMessageType         = REFEREE_MESSAGE.UNKNOWN;

  // the last heared playmode
  private PLAY_MODE       lastPlayModeMessageType        = PLAY_MODE.UNKNOWN;

  /**
   * the current playmode (!= lastHearedPlaymode)
   * 
   * @see robocup.component.worldmodel.PlayModeModel.correctPlayMode
   */
  private PLAY_MODE       currentPlayMode                = PLAY_MODE.BEFORE_KICK_OFF;

  private int             lastRefereeMessageArrivalTime  = -1;

  private int             lastPlayModeMessageArrivalTime = -1;

  // previous play mode
  private PLAY_MODE       previousPlayMode;

  // play mode of the last cycle
  private PLAY_MODE       lastCyclePlayMode;

  /**
   * Constructor.
   * 
   * @param world -
   *          the current world-model
   */
  public PlayModeModel(
      final WorldModel world) {

    this.world = world;
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.SenseBodyInfo)
   */
  public void update(final SenseBodyInfo info) {

    this.lastCyclePlayMode = this.currentPlayMode;
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.VisualInfo)
   */
  public void update(final VisualInfo info) {

    // if the player who does the kick off (number 10) didn't here the kick off
    // command but was able to accelerate we assume the command was sent
    if (this.world.getNumber() == 10 && this.world.getPlayMode() == PLAY_MODE.BEFORE_KICK_OFF) {
      if (this.world.getSelfRef().getSpeed().getLength() > 0.3 && this.world
          .getSelfRef().getLastState() == STATES.BEFORE_KICKOFF) {
        if (!PLAY_MODE.META_PENALTY_OWN.contains(this.world.getPlayMode()) && !PLAY_MODE.META_PENALTY_OTHER
            .contains(this.world.getPlayMode())) {
          System.out
              .println(this.world.getCycle() + " SET PLAY_MODE TO PLAY ON FOR NUMBER 10 (NO PLAY_ON HEARED)");
          this.correctPlayMode(PLAY_MODE.PLAY_ON);
        }
      }
    }

    // if the opponent scored and a teammate tells us he passed the ball we
    // assume that the PLAY_MODE->PLAY_ON message was overheared.
    if (this.currentPlayMode == PLAY_MODE.GOAL_KICK_OWN && this.world
        .getSelfRef().getLastHearedPlayer() == 1 && this.world.getSelfRef()
        .getLastHearedMessageType() == MESSAGES.KICKED_BALL) {
      if (this.world.getBallRef().getSpeed().getLength() > 0.5) {
        this.correctPlayMode(PLAY_MODE.PLAY_ON);
      }
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.RefereeInfo)
   */
  public void update(final RefereeInfo info) {

    this.lastRefereeMessageType = info.getRefereeMessageType();
    this.lastRefereeMessageArrivalTime = this.world.getBodyCycle();

    // some referee info always announce the same play mode so we set it here
    switch (info.getRefereeMessageType()) {
      case OFFSIDE_OTHER:
        this.correctPlayMode(PLAY_MODE.OFFSIDE_OTHER);
        break;

      case OFFSIDE_OWN:
        this.correctPlayMode(PLAY_MODE.OFFSIDE_OWN);
        break;
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.PlayModeInfo)
   */
  public void update(final PlayModeInfo info) {

    this.previousPlayMode = this.currentPlayMode;
    this.lastPlayModeMessageType = info.getPlayModeType();
    this.currentPlayMode = info.getPlayModeType();
    this.lastPlayModeMessageArrivalTime = this.world.getBodyCycle();

    // if the mode changes to a standard play mode the "kicked standard" flag
    // is set to "false" for all players
    if (info.getPlayModeType() != PLAY_MODE.PLAY_ON) {
      for (int i = 0; i < 22; i++) {
        this.world.getExpectedPlayers()[i].setKickedStandard(false);
        this.world.getAllPlayers()[i].setKickedStandard(false);
        this.world.getNextPlayers()[i].setKickedStandard(false);
      }
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.PlayerSayInfo)
   */
  public void update(final PlayerSayInfo info) {

  }

  /**
   * @return Returns the lastPlayModeMessage.
   */
  public PLAY_MODE getLastPlayModeMessageType() {

    return this.lastPlayModeMessageType;
  }

  /**
   * @return Returns the lastPlayModeMessageArrivalTime.
   */
  public int getLastPlayModeMessageCycle() {

    return this.lastPlayModeMessageArrivalTime;
  }

  /**
   * @return Returns the lastRefereeMessageArrivalTime.
   */
  public int getLastRefereeMessageArrivalTime() {

    return this.lastRefereeMessageArrivalTime;
  }

  /**
   * @return Returns the lastRefereeMessageType.
   */
  public REFEREE_MESSAGE getLastRefereeMessageType() {

    return this.lastRefereeMessageType;
  }

  /**
   * @return Returns the currentPlayMode.
   */
  public PLAY_MODE getCurrentPlayMode() {

    return this.currentPlayMode;
  }

  /**
   * sometimes we miss the play_mode message. this method is used for
   * correction.
   * 
   * i.e. if the ball is moving we have to be in PlayMode = PLAY_ON
   * 
   * @param play_mode
   */
  public void correctPlayMode(final PLAY_MODE play_mode) {

    this.currentPlayMode = play_mode;

  }

  /**
   * @return Returns the previousPlayMode.
   */
  public PLAY_MODE getPreviousPlayMode() {

    return this.previousPlayMode;
  }

  /**
   * @return Returns the lastCyclePlayMode.
   */
  public PLAY_MODE getLastCyclesPlayMode() {

    return this.lastCyclePlayMode;
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.PlayerTypesInfo)
   */
  public void update(final PlayerTypesInfo info) {

    // TODO Auto-generated method stub
    
  }
}
